<template>
    <div class="pro-upload-img-box">
        <div class="pro-upload-img-content">
            <!-- 已上传图片列表 -->
            <div class="upload-img-card" v-for="(item, index) in fileList" :key="index">
                <!-- 图片预览 -->
                <el-image class="img-sty" :preview-src-list="[item.url]" fit="cover" :src="item.url" alt="" />
                <!-- 删除按钮 -->
                <el-image v-if="!disabled"
                    src="https://static.wxb.com.cn/frontEnd/images/ideacome-vue3-component/img-close.png"
                    class="img-close" @click="handleRemove(item, index)" />
                <!-- 图片遮罩层 -->
                <div class="img-mask">
                    <el-image src="https://static.wxb.com.cn/frontEnd/images/ideacome-vue3-component/img-preview.png"
                        class="img-preview" />
                </div>
            </div>
            <!-- 上传组件 -->
            <el-upload v-loading="loading" ref="proUploadImgRef"
                :class="['pro-upload-img', { 'is-disabled': disabled }]" v-bind="uploadProps"
                :before-upload="beforeUpload" :on-success="handleSuccess" :on-error="handleError"
                :on-exceed="handleExceed">
                <slot>
                    <div class="upload-card">
                        <el-icon class="upload-icon">
                            <Plus />
                        </el-icon>
                        <div v-if="uploadText" class="upload-text">
                            {{ uploadText }}
                        </div>
                    </div>
                </slot>
            </el-upload>
        </div>
        <!-- 提示信息 -->
        <slot name="tip">
            <div class="upload-tip" v-if="tip">
                {{ tip }}
            </div>
        </slot>
    </div>
</template>

<script setup name="proUploadImg">
import { ref, computed } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';

// Props 定义
const props = defineProps({
    /** 上传地址 */
    action: {
        type: String,
        required: true,
    },
    /** 请求头 */
    headers: {
        type: Object,
        default: () => ({}),
    },
    /** 是否支持多选 */
    multiple: {
        type: Boolean,
        default: false,
    },
    /** 最大上传数量，0表示不限制 */
    limit: {
        type: Number,
        default: 0,
    },
    /** 接受的文件类型，如：.jpg,.png,.jpeg */
    accept: {
        type: String,
        default: '.jpg,.png,.jpeg',
    },
    /** 文件大小限制 */
    maxSize: {
        type: Number,
        default: 0,
    },
    /** 文件大小单位（KB/MB） */
    sizeUnit: {
        type: String,
        default: 'MB',
        validator: (value) => ['KB', 'MB'].includes(value),
    },
    /** 图片宽度限制 */
    width: {
        type: Number,
        default: 0,
    },
    /** 图片高度限制 */
    height: {
        type: Number,
        default: 0,
    },
    /** 上传提示文字 */
    uploadText: {
        type: String,
        default: '上传图片',
    },
    /** 上传提示说明 */
    tip: {
        type: String,
        default: '',
    },
    /** 是否禁用 */
    disabled: {
        type: Boolean,
        default: false,
    },
});
/** 初始文件列表 */
const fileList = defineModel('fileList', {
    type: Array,
    default: () => [],
});

// 事件定义
const emit = defineEmits(['success', 'error', 'exceed', 'remove']);

const proUploadImgRef = ref();
const loading = ref(false);

const uploadProps = computed(() => ({
    action: props.action,
    accept: props.accept,
    limit: props.limit,
    multiple: props.multiple,
    listType: 'picture-card',
    showFileList: false,
    headers: props.headers,
    fileList: fileList.value,
    disabled: props.disabled,
}));

/**
 * 验证图片尺寸是否符合要求
 * @param {number} width - 图片宽度
 * @param {number} height - 图片高度
 * @returns {boolean} 是否符合要求
 */
const validateImageSize = (width, height) => {
    if (props.width && props.height) {
        return width === props.width && height === props.height;
    }
    if (props.width) {
        return width === props.width;
    }
    if (props.height) {
        return height === props.height;
    }
    return true;
};

/**
 * 上传前校验
 * @param {File} file - 待上传的文件
 * @returns {Promise<boolean>} 是否通过校验
 */
const beforeUpload = async (file) => {
    // 校验文件类型
    const fileTypeList = props.accept
        .split(',')
        .map((item) => item.replace('.', ''));
    const fileType = file.name.split('.').pop();

    if (!fileTypeList.includes(fileType)) {
        ElMessage({
            message: `仅支持 ${fileTypeList.join('、')} 格式`,
            type: 'warning',
        });
        return false;
    }

    // 校验文件大小
    if (props.maxSize) {
        const fileSize = file.size / 1024;
        const maxSizeInKB =
            props.sizeUnit === 'MB' ? props.maxSize * 1024 : props.maxSize;
        if (fileSize > maxSizeInKB) {
            ElMessage({
                message: `大小不能超过 ${props.maxSize}${props.sizeUnit}！`,
                type: 'warning',
            });
            return false;
        }
    }

    // 校验图片尺寸
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.src = URL.createObjectURL(file);
        img.onload = () => {
            URL.revokeObjectURL(img.src);
            const { width, height } = img;

            if (!validateImageSize(width, height)) {
                const message =
                    props.width && props.height
                        ? `图片尺寸必须为 ${props.width}x${props.height}`
                        : props.width
                            ? `图片宽度必须为 ${props.width}px`
                            : `图片高度必须为 ${props.height}px`;

                ElMessage({
                    message,
                    type: 'warning',
                });
                reject(false);
                return;
            }
            loading.value = true;
            resolve(true);
        };
        img.onerror = () => {
            URL.revokeObjectURL(img.src);
            ElMessage({
                message: '图片加载失败',
                type: 'error',
            });
            reject(false);
        };
    });
};

/**
 * 上传成功回调
 * @param {Object} response - 服务器响应数据
 * @param {Object} uploadFile - 上传文件对象
 * @param {Array} uploadFiles - 上传文件列表
 */
const handleSuccess = (response, uploadFile, uploadFiles) => {
    loading.value = false;
    if (response.status === 0) {
        fileList.value.push({ url: response.data.fileUrl });
    } else {
        proUploadImgRef.value.handleRemove(uploadFile);
        ElMessage({
            message: response.msg || response.message || '上传失败',
            type: 'error',
        });
    }
    emit('success', response, uploadFile, uploadFiles);
};

/**
 * 上传失败回调
 * @param {Error} error - 错误信息
 * @param {Object} uploadFile - 上传文件对象
 * @param {Array} uploadFiles - 上传文件列表
 */
const handleError = (error, uploadFile, uploadFiles) => {
    loading.value = false;
    ElMessage({
        message: '上传失败',
        type: 'error',
    });
    emit('error', error, uploadFile, uploadFiles);
};

/**
 * 超出限制回调
 * @param {Array} files - 超出限制的文件列表
 * @param {Array} uploadFiles - 已上传的文件列表
 */
const handleExceed = (files, uploadFiles) => {
    ElMessage({
        message: `最多只能上传 ${props.limit} 张图片`,
        type: 'warning',
    });
    emit('exceed', files, uploadFiles);
};

/**
 * 移除图片
 * @param {Object} file - 要移除的文件对象
 * @param {number} index - 文件索引
 */
const handleRemove = (file, index) => {
    fileList.value = fileList.value.filter(e => e.url !== file.url);
    proUploadImgRef.value.handleRemove(file);
    emit('remove', file);
};
</script>

<style lang="scss" scoped>
.pro-upload-img-box {
    .pro-upload-img-content {
        display: flex;
        flex-wrap: wrap;

        // 已上传图片卡片样式
        .upload-img-card {
            width: 100px;
            height: 100px;
            position: relative;
            margin: 0 12px 12px 0;

            // 图片样式
            .img-sty {
                width: 100%;
                height: 100%;
                overflow: hidden;
                border-radius: 6px;
            }

            // 删除按钮样式
            .img-close {
                position: absolute;
                right: -6px;
                top: -6px;
                width: 20px;
                height: 20px;
                cursor: pointer;
                z-index: 2;
            }

            // 遮罩层样式
            .img-mask {
                background: rgba(0, 0, 0, 0.3);
                border-radius: 6px;
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                pointer-events: none;

                .img-preview {
                    position: absolute;
                    right: 8px;
                    bottom: 8px;
                    width: 20px;
                    height: 20px;
                    pointer-events: none;
                }
            }
        }

        // 禁用状态样式
        .is-disabled {
            :deep(.el-upload--picture-card) {
                cursor: not-allowed;
            }
        }

        // 上传按钮样式
        .pro-upload-img {
            margin-bottom: 12px;

            .upload-card {
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;

                .upload-icon {
                    font-size: 20px;
                    color: #8c939d;
                    text-align: center;
                    line-height: 100px;
                }

                .upload-text {
                    line-height: 24px;
                    color: #8c939d;
                    font-size: 14px;
                    text-align: center;
                    margin-top: 10px;
                }
            }
        }

        // 上传组件样式覆盖
        :deep(.el-upload--picture-card) {
            width: 100px;
            height: 100px;
            background-color: #fff;
        }

        :deep(.el-upload-list__item) {
            width: auto;
            height: auto;
            overflow: visible;
        }
    }

    // 提示文字样式
    .upload-tip {
        font-size: 12px;
        color: #909399;
    }
}
</style>